/*
 * @Author: xiaohao
 * @Date: 2025-11-03 09:08:19
 * @LastEditors: xiaohao
 * @LastEditTime: 2025-11-05 21:50:57
 * @FilePath: /Project/ConcurrentMemoryPool/src/PageCache.cpp
 * @Description:
 */

#include "ConcurrentMemoryPool/PageCache.h"

PageCache &PageCache::GetPageCacheInstance()
{
    static PageCache instance;
    return instance;
}

// 获取一个k页的span
Span *PageCache::NewSpan(size_t k)
{
    // 检查k的合法性
    assert(k > 0);

    // 大于128页的span, 直接向堆申请内存
    if (k > NPAGES - 1)
    {
        void* ptr = SystemAlloc(k); 
        Span* span = _spanPool.New();
        span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
        span->_numPages = k;
        
        _idSpanMap[span->_pageId] = span;

        return span;
    }

    // 1. 先在对应的_spanLists[k]中找 span
    if (_spanLists[k].Empty() != true)
    {
        return _spanLists[k].PopFront();
    }
    
    // 2.检查后面的桶有没有span, 如果有可以进行切分
    for (size_t i = k + 1; i < NPAGES; ++i)
    {
        if (_spanLists[i].Empty() != true)
        {
            // 取出一个更大的Span
            Span *nSpan = _spanLists[i].PopFront();
            // 创建一个k页的新Span
            Span *kSpan = _spanPool.New();
            if (!kSpan)
            {
                throw std::bad_alloc();
            }

            // 切分：大Span分为k页和剩余(i-k)页
            kSpan->_pageId = nSpan->_pageId;
            kSpan->_numPages = k;

            nSpan->_pageId += k;
            nSpan->_numPages -= k;

            /// 剩余部分放回对应页数的链表
            _spanLists[nSpan->_numPages].PushFront(nSpan);
            // 更新剩余Span的页ID映射
            _idSpanMap[nSpan->_pageId] = nSpan;
            _idSpanMap[nSpan->_pageId + nSpan->_numPages - 1] = nSpan;

            // 建立Id和span的映射, 方便CentralCache回收小块内存时, 找到对应的span
            for (PAGE_ID i = 0; i < kSpan->_numPages; ++i)
            {
                _idSpanMap[kSpan->_pageId + i] = kSpan;
            }

            return kSpan;
        }
    }

    // 3. 没有大页的span,向系统申请128页（NPAGES-1=128）的新内存
    Span *bigSpan = _spanPool.New();
    if (!bigSpan)
    {
        throw std::bad_alloc();
    }

    // 申请128页内存（使用统一的SystemAlloc函数，跨平台兼容）
    void *ptr = SystemAlloc(NPAGES - 1); // NPAGES-1=128页

    // 初始化大Span的页信息
    bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT; // 起始页ID = 地址 / 页大小
    bigSpan->_numPages = NPAGES - 1; // 128页的span

    // 将新申请的大Span放入对应页数的链表
    _spanLists[bigSpan->_numPages].PushFront(bigSpan);
    
    // 递归调用，从新申请的大Span中切分k页的span返回
    return NewSpan(k); 
}

Span *PageCache::MapObjectToSpan(void *obj)
{
    // 1. 先获取obj的地址
    PAGE_ID pageid = (PAGE_ID)obj >> PAGE_SHIFT;
    // 2. 再根据pageid找到对应的span
    auto ret = _idSpanMap.find(pageid);
    if (ret != _idSpanMap.end())
    {
        return ret->second;
    }
    else
    {
        perror("MapObjectToSpan");
        return nullptr;
    }
}

// 释放空闲span回到PageCache, 合并相邻的span
void PageCache::ReleaseSpanToPageCache(Span *span)
{
    // 大于128页的span, 直接还给堆
    if (span->_numPages > NPAGES - 1)
    {
        // 释放大页内存
        void *ptr = (void*)(span->_pageId << PAGE_SHIFT);
        SystemFree(ptr, span->_numPages);
        _spanPool.Delete(span);
        return;
    }
    // 对span前面的页尝试进行合并, 缓解内存(外)碎片问题
   while (true)
   {
       PAGE_ID prevId = span->_pageId - 1;
       auto prevRet = _idSpanMap.find(prevId);

       // 前面的页号没有, 不进行合并
       if (prevRet == _idSpanMap.end())
       {
           break;
       }

       Span *prevSpan = prevRet->second;
       // 前面的页号有, 但是前面的页号不是空闲的, 不进行合并
       if (prevSpan->_isUsed == true)
       {
           break;
       }

       // 前面的span的大小超过128页, 不进行合并
       if (prevSpan->_numPages + span->_numPages > NPAGES - 1)
       {
           break;
       }

       // 前面的span和当前span合并
       span->_pageId = prevSpan->_pageId;
       span->_numPages += prevSpan->_numPages;

       // prevSpan从_spanLists中删除
       _spanLists[prevSpan->_numPages].Erase(prevSpan);
       _spanPool.Delete(prevSpan);
   }

   // 对span后面的页尝试进行合并, 缓解内存(外)碎片问题
   while (true)
   {
       PAGE_ID nextId = span->_pageId + span->_numPages;
       auto nextRet = _idSpanMap.find(nextId);

       // 后面的页号没有, 不进行合并
       if (nextRet == _idSpanMap.end())
       {
           break;
       }

       Span *nextSpan = nextRet->second;
       // 后面的页号有, 但是后面的页号不是空闲的, 不进行合并
       if (nextSpan->_isUsed == true)
       {
           break;
       }

       // 后面的span的大小超过128页, 不进行合并
       if (nextSpan->_numPages + span->_numPages > NPAGES - 1)
       {
           break;
       }

       // 后面的span和当前span合并
       span->_numPages += nextSpan->_numPages;

       // nextSpan从_spanLists中删除
       _spanLists[nextSpan->_numPages].Erase(nextSpan);
       _spanPool.Delete(nextSpan);
   }

   _spanLists[span->_numPages].PushFront(span);
   span->_isUsed = false;
   _idSpanMap[span->_pageId] = span;
   _idSpanMap[span->_pageId + span->_numPages - 1] = span;
}